package org.robot.component;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class Page {
	public static final String URLTEXT = "_urlText_"; 
	
    private Document doc;
    private Attachment attachment;
    private List<Attachment> nextAttachmentList = new LinkedList<>();
    private Predicate<Element> nextLinksFilter = e -> true;
    private Function<Element, Map<String, Object>> nextInfoMap = e -> createMap(URLTEXT, e.text());
    private Function<String, String> nextUrlFunc = Function.identity();

    public Page(Document doc, Attachment attachment) {
        this.doc = doc;
        this.attachment = attachment;
    }
    
    public Map<String, Object> createMap(String key, Object value) {
    	Map<String, Object> map = new HashMap<>();
    	map.put(key, value);
    	return map;
    }

    /**
     * 应该规则，返回符合规则的elements
     * @author pdz
     * @date 2019年1月7日
     * @param rule
     * @return
     */
    public Stream<Element> eleStream(Rule rule) {
    	return select(rule.getSelector()).stream()
    		.filter(ele -> ele.attr(rule.getAttr()).matches(rule.getRegex()))
    		.filter(nextLinksFilter);
    }
    
    public List<Attachment> applyRule(Rule rule) {
    	return eleStream(rule)
    			.map(e -> new Attachment(nextUrlFunc.apply(e.attr(rule.getAttr())), attachment).setInfoMap(putIfAbsent(nextInfoMap.apply(e), e.text())))
    			.distinct()
    			.collect(Collectors.toList());
    }
    
    private Map<String, Object> putIfAbsent(Map<String, Object> map, Object value) {
    	map.putIfAbsent(URLTEXT, value);
    	return map;
    }
    
    /**
     * @param rule 该参数 通过调用Rule中的静态方法得到
     */
    public void addNext(Rule rule) {
    	addNext(applyRule(rule));
    }
    
    /**
     * 如果是手动添加就调用这个方法
     * addByHand方法调用不带参数才调用这个带type参数的addNext方法，否则调用不带type的addNext方法
     * @param links 将要爬取的所有链接
     * @param type 将要爬取的页面类型
     */
    @Deprecated
    public void addNext(Collection<String> links, String type) {
        links.forEach(url -> addNext(url, type));
    }
    
    /**
     * addByHand方法调用不带参数才调用这个带type参数的addNext方法，否则调用不带type的addNext方法
     */
    @Deprecated
    public void addNext(String url, String type) {
    	nextAttachmentList.add(new Attachment(url, type, attachment));
    }
    
    public void addNextLinks(Collection<String> links) {
    	links.forEach(url -> addNext(url));
    }
    
    public void addNext(String url) {
    	nextAttachmentList.add(new Attachment(url, attachment));
    }

    public void addNext(Collection<Attachment> attachments) {
    	nextAttachmentList.addAll(attachments);
    }
    
    /**
     * 可以循环调用该方法直到所有的下层链接添加完毕
     * addByHand方法调用不带参数才调用这个带type参数的addNext方法，否则调用不带type的addNext方法
     * @param url
     * @param infoMap 携带到下层的信息
     * @param type 
     */
    @Deprecated
    public void addNext(String url, Map<String, Object> infoMap, String type) {
        Attachment attach = new Attachment(url, type, attachment, infoMap);
        nextAttachmentList.add(attach);
    }
    
    public void addNext(String url, Map<String, Object> infoMap) {
    	Attachment attach = new Attachment(url, attachment).setInfoMap(infoMap);
    	nextAttachmentList.add(attach);
    }
    
    public void addNext(Attachment attachment, Map<String, Object> infoMap) {
    	attachment.setInfoMap(infoMap);
    	nextAttachmentList.add(attachment);
    }
    
    /**
     * addByHand方法调用不带参数才调用这个带type参数的addNext方法，否则调用不带type的addNext方法
     */
    @Deprecated
    public void addNext(String url, String key, Object value, String type) {
    	Map<String,Object> map = new HashMap<>();
    	map.put(key, value);
    	addNext(url, map, type);
    }
    
    public void addNext(String url, String key, Object value) {
    	Map<String,Object> map = new HashMap<>();
    	map.put(key, value);
    	addNext(url, map);
    }
    
    public void addNext(Attachment attachment, String key, Object value) {
    	Map<String,Object> map = new HashMap<>();
    	map.put(key, value);
    	addNext(attachment, map);
    }

    public boolean isSeed() {
        return attachment.isSeed();
    }

    public boolean typeEquals(String type) {
        return attachment.typeEquals(type);
    }

    public String url() {
        return attachment.getUrl();
    }

    public Elements select(String cssQuery) {
        return doc.select(cssQuery);
    }

    public String html() {
        return doc.html();
    }

    public String text() {
        return doc.text();
    }
    
    /**
     * 返回上一层携带的数据
     * @author pdz
     * @date 2019年1月7日
     * @param key
     * @return
     */
    public Object get(String key) {
    	return attachment.get(key);
    }
    
    /**
     * 添加键值对到当前Attachment里面的infoMap中
     * @author pdz
     * @date 2019年1月23日
     * @param key
     * @param value
     * @return
     */
    public Object put(String key, Object value) {
    	return attachment.put(key, value);
    }
    
    /**
     * 链接所在标签的文本
     * @author pdz
     * @date 2019年1月8日
     * @return
     */
    public String urlText() {
    	return (String) get(URLTEXT);
    }

    /**
     * 返回前prevNum个Attachment，不存在返回null
     * @param prevNum
     * @return
     */
    public Attachment prevAttachment(int prevNum) {
        return attachment.prevAttachment(prevNum);
    }

    public Attachment prevAttachment() {
        return attachment.getPrev();
    }

    public String prevUrl() {
        return attachment.prevUrl();
    }

    /**
     * 返回前prevNum层的url，从1开始
     * @param prevNum
     * @return
     */
    public String prevUrl(int prevNum) {
        return attachment.prevUrl(prevNum);
    }
    
    public String pdfUrl() {
    	return (String) get("pdfUrl");
    }
    
    public String pdfContent() {
    	return (String) get("pdfContent");
    }

    @SuppressWarnings("unchecked")
	public Set<String> emailSet() {
    	return (Set<String>) get("emailSet");
    }
    
    public Document getDoc() {
        return doc;
    }

    public void setDoc(Document doc) {
        this.doc = doc;
    }

    public Attachment getAttachment() {
        return attachment;
    }
    public List<Attachment> getNextAttachmentList() {
		return nextAttachmentList;
	}

    /**
     * 筛选下层链接的条件
     * @author pdz
     * @date 2019年1月8日
     * @param nextLinksFilter
     * @return
     */
    public Page setNextLinksFilter(Predicate<Element> nextLinksFilter) {
		this.nextLinksFilter = nextLinksFilter;
		return this;
	}
    /**
     * 可以调用createMap(String key, Object value)方法，会返回一个map
     * @author pdz
     * @date 2019年1月14日
     * @param nextInfoMap
     * @return
     */
    public Page setNextInfoMap(Function<Element, Map<String, Object>> nextInfoMap) {
		this.nextInfoMap = nextInfoMap;
		return this;
	}
    
    /**
     * 对链接进行转换，有的中间链接可能只需要转换一个词就能得到你想要的链接，这时候可以调用这个方法
     * @author pdz
     * @date 2019年1月16日
     * @param nextUrlFunc 参数为链接，返回的依然是链接，默认直接返回，不做任何改变
     * @return
     */
    public Page setNextUrlFunc(Function<String, String> nextUrlFunc) {
		this.nextUrlFunc = nextUrlFunc;
		return this;
	}
}
